package com.sunshine.service.impl;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.sunshine.domain.CommodityAndStock;
import com.sunshine.mapper.PayAndRepertoryMapper;
import com.sunshine.mapper.PayMapper;
import com.sunshine.mapper.UserAccountMapper;
import com.sunshine.service.PayService;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Service
public class PayServiceImpl implements PayService {

    @Autowired
    private PayMapper payMapper;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private PayAndRepertoryMapper payAndRepertoryMapper;

    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private RedissonClient redissonClient;

    // 库存更新队列
    private static final String STOCK_UPDATE_QUEUE_KEY = "stock_update_queue";
    // 商品ID前缀
    private static final String COMMODITY_KEY_PREFIX = "commodity:";
    // 商品锁前缀
    private static final String COMMODITY_LOCK_PREFIX = "commodity_lock:";

    /**
     * 异步处理库存更新任务
     * 定时从Redis队列中取出库存更新任务并更新到MySQL数据库
     */
    @Scheduled(fixedDelay = 5000) // 每5秒执行一次
    public void processStockUpdates() {
        //Redis的列表中取出一个库存更新任务 stock_update_queue
        //获取队列中的第一个元素
        Object updateInfo = redisTemplate.opsForList().rightPop(STOCK_UPDATE_QUEUE_KEY);

        if (updateInfo != null) {
            try {
                // 处理库存更新到MySQL
                if (updateInfo instanceof Map) {
                    Map<String, Object> updateMap = (Map<String, Object>) updateInfo;
                    Integer commodityId = (Integer) updateMap.get("commodityId");
                    Integer quantity = (Integer) updateMap.get("quantity");

                    // 准备更新参数
                    Map<String, Object> params = new HashMap<>();
                    params.put("commodityId", commodityId);
                    params.put("purchaseQuantity", quantity);

                    // 更新MySQL库存
                    int result = payAndRepertoryMapper.updateRepertory(params);

                    if (result > 0) {
                        System.out.println("成功更新商品ID为 " + commodityId + " 的库存，减少数量: " + quantity);
                    } else {
                        System.err.println("更新商品ID为 " + commodityId + " 的库存失败");
                        // 可以将失败的任务重新放入队列或记录到错误日志中
                    }
                }
            } catch (Exception e) {
                System.err.println("处理库存更新任务时发生异常: " + e.getMessage());
                e.printStackTrace();
                // 可以将失败的任务重新放入队列或记录到错误日志中
            }
        }
    }

    @Override
    @Transactional
    @SentinelResource(value = "pay", blockHandler = "handlePayFallback")
    public Map<String, Object> pay(Integer commodityId, Integer userId, Integer quantity) {
        String commodityKey = COMMODITY_KEY_PREFIX + commodityId;
        String commodityLockKey = COMMODITY_LOCK_PREFIX + commodityId;
        CommodityAndStock commodity = null;
        //获取锁
        RLock commoditylock = redissonClient.getLock(commodityLockKey);
/*        // 先从Redis中获取商品信息
        Object cachedCommodity = redisTemplate.opsForValue().get(commodityKey)*/
        ;

        try {
            //尝试加锁 最多等待10秒，上锁后最多持有30秒
            commoditylock.tryLock(10, TimeUnit.SECONDS);
            // 先从Redis中获取商品信息
            Object cachedCommodity = redisTemplate.opsForValue().get(commodityKey);
            //查库存
            if (cachedCommodity != null) {
                // 如果Redis中有数据，直接使用
                commodity = (CommodityAndStock) cachedCommodity;
            } else {
                // 如果Redis中没有数据，从数据库查询
                commodity = payAndRepertoryMapper.getCommodityAndStock(commodityId);
                if (commodity == null) {
                    throw new RuntimeException("商品不存在");
                }
                // 将查询到的数据存入Redis缓存
                redisTemplate.opsForValue().set(commodityKey, commodity, 100, TimeUnit.SECONDS);
            }
            // 检查库存
            if (commodity.getStockQuantity() < quantity) {
                throw new RuntimeException("商品库存不足");
            }
            // 检查余额
            //查用户余额
            BigDecimal balance = userAccountMapper.selectByUserId(userId).getBalance();
            //计算需要多少钱才能买到
            BigDecimal totalPrice = commodity.getPrice().multiply(new BigDecimal(quantity));
            if (balance.compareTo(totalPrice) < 0) {
                throw new RuntimeException("余额不足");
            }
            // 扣减库存
            Integer i = commodity.getStockQuantity() - quantity;
            //写入Redis
            commodity.setStockQuantity(i);
            redisTemplate.opsForValue().set(commodityKey, commodity, 20, TimeUnit.SECONDS);
            // 扣减余额
            userAccountMapper.deductBalance(userId, totalPrice);
            // 生成支付记录
            payAndRepertoryMapper.insertPayRecord(Map.of(
                    "userId", userId,
                    "productId", commodityId,
                    "quantity", quantity,
                    "amount", totalPrice
            ));
            // 异步更新MySQL数据库（通过消息队列或定时任务）
            // 创建一个Map对象，用于存储库存更新信息,推送到消息队列中
            Map<String, Object> updateInfo = new HashMap<>();
            updateInfo.put("commodityId", commodityId);
            updateInfo.put("quantity", quantity);
            // 记录需要更新的库存信息到一个队列中
            redisTemplate.opsForList().leftPush(STOCK_UPDATE_QUEUE_KEY, updateInfo);
        } catch (InterruptedException e) {
            throw new RuntimeException("系统繁忙");
        } finally {
            //判断是否持有锁
            if (commoditylock.isHeldByCurrentThread()) {
                //释放锁
                commoditylock.unlock();
            }
        }


        return Map.of("success", true, "message", "支付处理中，库存已预扣");
    }

    public Map<String, Object> handlePayFallback(Integer commodityId, Integer userId, Integer quantity, Throwable throwable) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 500);
        result.put("msg", "服务繁忙，请稍后再试");
        return result;
    }
}